package com.eternal.base.protocol;

import com.eternal.base.concat.DeviceModel;
import com.eternal.base.concat.DeviceSetting;
import com.eternal.base.concat.DeviceTFP;
import com.eternal.base.concat.HistoryInfo;
import com.eternal.base.concat.PortInfo;
import com.eternal.base.database.entity.History;
import com.eternal.base.database.entity.Log;
import com.eternal.base.database.entity.Notification;
import com.eternal.base.global.BluetoothKey;
import com.eternal.base.utils.ByteUtils;
import com.eternal.base.utils.CRCUtil;
import com.eternal.framework.utils.KLog;
import com.google.common.primitives.UnsignedBytes;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.concurrent.TimeUnit;

import retrofit2.http.HEAD;

public class EFamilialResolution extends BaseProtocol {

    public static byte[] setTimeData(int sequence) {
        byte[] timeData = new byte[11];
        Calendar calendar = Calendar.getInstance();
        timeData[0] = BluetoothKey.DEVICE_CLOCK_TIME;
        timeData[1] = 7;
        timeData[2] = (byte) (calendar.get(Calendar.YEAR) % 100);
        timeData[3] = (byte) (calendar.get(Calendar.MONTH) + 1);
        timeData[4] = (byte) calendar.get(Calendar.DAY_OF_MONTH);
        timeData[5] = (byte) calendar.get(Calendar.DAY_OF_WEEK);
        timeData[6] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
        timeData[7] = (byte) calendar.get(Calendar.MINUTE);
        timeData[8] = (byte) calendar.get(Calendar.SECOND);
        timeData[9] = (byte) 0xFF;
        timeData[10] = (byte)0x0;
        return addHead(timeData, BluetoothKey.COMMAND_SET, sequence);
    }

    /**
     * 获得查询温度，负载，等包数据
     *
     * @param command  查询方式
     * @param sequence 序列
     * @return 包
     */
    public static byte[] getTFPData(byte command, int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_CURRENT_TEMP_HUM_STATUS,
                BluetoothKey.DEVICE_CURRENT_LOAD_CONDITION,
                BluetoothKey.DEVICE_TMP_UNIT,
                BluetoothKey.DEVICE_PORT_CHOOSE,
                BluetoothKey.DEVICE_PORT_COUNT,
                BluetoothKey.DEVICE_ALL_PORT_TYPE,
                (byte)0xFF,(byte)0x0
        }, command, sequence);
    }

    public static DeviceTFP parseTFP(byte[] packet) {
        DeviceTFP result = new DeviceTFP();
        packet = check(packet);
        if (packet[0] == BluetoothKey.DEVICE_CURRENT_TEMP_HUM_STATUS) {
            result.tmpState = (byte) ByteUtils.getBits(packet[2], 4, 2);
            result.humState = (byte) ByteUtils.getBits(packet[2], 6, 2);
            result.tmp = ByteUtils.getShort(packet, 3);
            result.per = ByteUtils.getShort(packet, 5);
        } else {
            return null;
        }

        if (packet[7] == BluetoothKey.DEVICE_CURRENT_LOAD_CONDITION) {
            result.fanType = packet[9];
            //解析风扇档位
            result.fan = ByteUtils.getBits(packet[10], 4, 4);
            //解析当前风扇上升下降趋势
            result.fanState = (byte) ByteUtils.getBits(packet[10], 0, 2);
        } else {
            return null;
        }

        if (packet[14] == BluetoothKey.DEVICE_TMP_UNIT) {
            result.isDegree = !ByteUtils.getBit(packet[16], 7);
        } else {
            return null;
        }

        if (packet[17] == BluetoothKey.DEVICE_PORT_CHOOSE) { // 选中端口
            result.choosePort = packet[19];
        } else {
            return null;
        }

        // 解析各个端口负载
        if (packet[31] == BluetoothKey.DEVICE_ALL_PORT_TYPE) {
            int size = 33 + packet[32];
            for (int i = 33 ; i < size ; i+=7) {
                PortInfo portInfo = new PortInfo();
                portInfo.id = packet[i];
                portInfo.isPlug = packet[i+1] != (byte)0xff;
                portInfo.fanType = packet[i+2];
                //解析当前风扇上升下降趋势
                portInfo.fanState = (byte) ByteUtils.getBits(packet[i+3], 0, 2);
                //解析风扇档位
                portInfo.fan = ByteUtils.getBits(packet[i+3], 4, 4);
                result.portList.add(portInfo);
            }
        } else {
            return null;
        }
        return result;
    }
    /**
     * 获得查询温度单位
     *
     * @param sequence 序列
     * @return 包
     */
    public static byte[] getTempUnitData(byte port, int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_TMP_UNIT,
                (byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getAllModelData(byte port, int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_CURRENT_OPERATING_MODE,
                BluetoothKey.DEVICE_FAN_GEAR_BY_OFF,
                BluetoothKey.DEVICE_FAN_GEAR_BY_ON,
                BluetoothKey.DEVICE_TEMP_HUM_STATUS_BY_AUTO,
                BluetoothKey.DEVICE_DURATION_BY_TIMER_TO_ON,
                BluetoothKey.DEVICE_DURATION_BY_TIMER_TO_OFF,
                BluetoothKey.DEVICE_DURATION_BY_CYCLE,
                BluetoothKey.DEVICE_DURATION_BY_SCHED,
                (byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] setModelData(byte port, DeviceModel model, int sequence) {
        byte[] packet;
        switch (model.workType) {
            case BluetoothKey.MODE_OFF:
                packet = new byte[]{0x11, 0x1, model.typeOff};
                break;
            case BluetoothKey.MODE_ON:
                packet = new byte[]{0x12, 0x1, model.typeOn};
                break;
            case BluetoothKey.MODE_AUTO:
                if (model.isDegree) {
                    packet = new byte[]{0x13, 0x7, ProtocolTransformer.getTmpSwitch(model),
                            (byte) Math.round(ProtocolTransformer.getFah(model.autoHighTmp)), model.autoHighTmp,
                            (byte) Math.round(ProtocolTransformer.getFah(model.autoLowTmp)), model.autoLowTmp,
                            model.autoHighHum, model.autoLowHum};
                } else {
                    packet = new byte[]{0x13, 0x7, ProtocolTransformer.getTmpSwitch(model),
                            model.autoHighTmp, (byte) Math.round(ProtocolTransformer.getDegree(UnsignedBytes.toInt(model.autoHighTmp))),
                            model.autoLowTmp, (byte) Math.round(ProtocolTransformer.getDegree(UnsignedBytes.toInt(model.autoLowTmp))),
                            model.autoHighHum, model.autoLowHum};
                }
                break;
            case BluetoothKey.MODE_TIME_OFF:
                packet = new byte[6];
                packet[0] = 0x15;
                packet[1] = 4;
                int value = model.timeOff * 60;
                setSecond(packet, 2, value);
                break;
            case BluetoothKey.MODE_TIME_ON:
                packet = new byte[6];
                packet[0] = 0x14;
                packet[1] = 4;
                value = model.timeOn * 60;
                setSecond(packet, 2, value);
                break;
            case BluetoothKey.MODE_CYCLE:
                packet = new byte[10];
                packet[0] = 0x16;
                packet[1] = 8;
                value = model.cycleOn * 60;
                setSecond(packet, 2, value);
                value = model.cycleOff * 60;
                setSecond(packet, 6, value);
                break;
            default:
                packet = new byte[6];
                packet[0] = 0x17;
                packet[1] = 4;
                value = model.schedOn;
                if (value == 1440) {
                    value = 1439;
                }
                packet[2] = (byte) (value / 60);
                packet[3] = (byte) (value % 60);
                value = model.schedOff;
                if (value == 1440) {
                    value = 1439;
                }
                packet[4] = (byte) (value / 60);
                packet[5] = (byte) (value % 60);
        }

        byte[] data = new byte[2 + packet.length];
        System.arraycopy(packet, 0, data, 0, packet.length);
        data[packet.length] = (byte)0xFF;
        data[packet.length +1] = port;

        return addHead(data, BluetoothKey.COMMAND_SET, sequence);
    }

    public static byte[] setModelData(byte port, byte model, int sequence) {
        return addHead(new byte[]{
                0x10, 1, model,(byte)0xFF,port
        }, BluetoothKey.COMMAND_SET, sequence);
    }

    private static void setSecond(byte[] data, int start, int seconds) {
        data[start] = (byte) (seconds >> 24 & 0xff);
        data[start + 1] = (byte) (seconds >> 16 & 0xff);
        data[start + 2] = (byte) (seconds >> 8 & 0xff);
        data[start + 3] = (byte) (seconds & 0xff);
    }

    public static byte[] setChoosePort(byte port, int sequence) {
        byte[] data = new byte[7];
        // 选择端口
        data[0] = BluetoothKey.DEVICE_PORT_CHOOSE;
        data[1] = 0x5;
        data[2] = port;
        data[3] = 0x0;
        data[4] = 0x0;
        data[5] = 0x0;
        data[6] = 0x0;
        return addHead(data, BluetoothKey.COMMAND_SET, sequence);
    }

    public static byte[] getModelData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_CURRENT_OPERATING_MODE,
                BluetoothKey.DEVICE_CURRENT_TEMP_HUM_STATUS,
                BluetoothKey.DEVICE_CURRENT_LOAD_CONDITION,
                BluetoothKey.DEVICE_TMP_UNIT,(byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getOffModelData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_FAN_GEAR_BY_OFF,(byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getOnModelData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_FAN_GEAR_BY_ON,(byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getAutoModelData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_TEMP_HUM_STATUS_BY_AUTO,(byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getTimeOnModelData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_DURATION_BY_TIMER_TO_ON,
                BluetoothKey.DEVICE_TIME_REMAINING_BY_CURRENT_MODE,(byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getTimeOffModelData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_DURATION_BY_TIMER_TO_OFF,
                BluetoothKey.DEVICE_TIME_REMAINING_BY_CURRENT_MODE,(byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getCycleModelData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_DURATION_BY_CYCLE,
                BluetoothKey.DEVICE_TIME_REMAINING_BY_CURRENT_MODE,(byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getSchedModelData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_DURATION_BY_SCHED,(byte)0xFF,port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] getLogStart(byte port, long start, long now, int id, int sequence) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTimeInMillis(start);
        byte[] data = new byte[16];
        data[0] = (byte) (calendar.get(Calendar.YEAR) - 2000);
        data[1] = (byte) (calendar.get(Calendar.MONTH)+1);
        data[2] = (byte) calendar.get(Calendar.DAY_OF_MONTH);
        data[3] = (byte) calendar.get(Calendar.HOUR_OF_DAY);
        data[4] = (byte) calendar.get(Calendar.MINUTE);
        data[5] = (byte) calendar.get(Calendar.SECOND);
        int sub= (int) (TimeUnit.MILLISECONDS.toSeconds(now)-TimeUnit.MILLISECONDS.toSeconds(start));
        data[6] = (byte) ((sub >> 24) & 0xff);
        data[7] = (byte) ((sub >> 16) & 0xff);
        data[8] = (byte) ((sub >> 8) & 0xff);
        data[9] = ((byte) (sub & 0xff));
        data[10] = (byte) ((id >> 24) & 0xff);
        data[11] = (byte) ((id >> 16) & 0xff);
        data[12] = (byte) ((id >> 8) & 0xff);
        data[13] = (byte) (id & 0xff);
        data[14] = (byte)0xff;
        data[15] = port;
        return addHead(data, BluetoothKey.COMMAND_LOG_START, sequence);
    }

    public static byte[] getLog(byte port, int max, int index, int sequence) {
        byte[] data = new byte[10];
        data[0] = (byte) ((max >> 24) & 0xff);
        data[1] = (byte) ((max >> 16) & 0xff);
        data[2] = (byte) ((max >> 8) & 0xff);
        data[3] = (byte) (max & 0xff);
        data[4] = (byte) ((index >> 24) & 0xff);
        data[5] = (byte) ((index >> 16) & 0xff);
        data[6] = (byte) ((index >> 8) & 0xff);
        data[7] = (byte) (index & 0xff);
        data[8] = (byte) 0xff;
        data[9] = port;
        return addHead(data, BluetoothKey.COMMAND_LOG, sequence);
    }

    public static int parseLogSize(byte[] packet) {
        packet = check(packet);
        int size = 0;
        size += (packet[0] & 0xff) << 24;
        size += (packet[1] & 0xff) << 16;
        size += (packet[2] & 0xff) << 8;
        size += packet[3] & 0xff;
        return size;
    }

    public static byte[] getNotificationData(byte port, int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_SET_THE_RELEVANT_AUTOMATION_MESSAGE,
                BluetoothKey.DEVICE_SET_THE_RELEVANT_ALARM_MESSAGE,
                BluetoothKey.DEVICE_SET_THE_RELEVANT_NOTIFICATION_MESSAGE,
                (byte)0xFF, port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static List<Notification> parseNotifications(byte port, byte[] packet) {
        packet = check(packet);
        Notification n;
        List<Notification> list = new ArrayList<>();
        int i = 0;
        while (i < packet.length) {
            if (packet.length < i + 4) {
//                KLog.e("parse notification err");
                break;
            }
            if(packet[i+1]==0){
                i+=2;
                continue;
            }
            n = new Notification();
            n.port = port;
            n.id = ByteUtils.getBits(packet[i + 2], 2, 6);
            n.open = !ByteUtils.getBit(packet[i + 2], 1);
            if (packet[i] == BluetoothKey.DEVICE_SET_THE_RELEVANT_AUTOMATION_MESSAGE) {
                if (packet.length < i + 24) {
//                    KLog.e("parse notification err");
                    break;
                }
                n.type = BluetoothKey.AUTOMATION;
                n.start = (char) (packet[i + 4] * 60 + packet[i + 5]);
                n.end = (char) (packet[i + 6] * 60 + packet[i + 7]);
                n.model = packet[i + 8];
                n.cycleOn = (char) ((((packet[i + 10] & 0xff) << 16) + ((packet[i + 11] & 0xff) << 8) + (packet[i + 12] & 0xff)) / 60);
                n.cycleOff = (char) ((((packet[i + 14] & 0xff) << 16) + ((packet[i + 15] & 0xff) << 8) + (packet[i + 16] & 0xff)) / 60);
                n.tmpHum = packet[i + 17];
                n.hTmpF = packet[i + 18];
                n.hTmpC = packet[i + 19];
                n.lTmpF = packet[i + 20];
                n.lTmpC = packet[i + 21];
                n.hHum = packet[i + 22];
                n.lHum = packet[i + 23];
                i += 24;
            } else {
                if (packet.length < i + 13) {
//                    KLog.e("parse notification err");
                    break;
                }
                if (packet[i] == BluetoothKey.DEVICE_SET_THE_RELEVANT_ALARM_MESSAGE) {
                    n.type = BluetoothKey.ALARMS;
                } else {
                    n.type = BluetoothKey.NOTIFICATIONS;
                }
                n.model = packet[i + 3];
                n.tmpHum = packet[i + 4];
                n.hTmpF = packet[i + 5];
                n.hTmpC = packet[i + 6];
                n.lTmpF = packet[i + 7];
                n.lTmpC = packet[i + 8];
                n.hHum = packet[i + 9];
                n.lHum = packet[i + 10];
                i += 13;
            }
            list.add(n);
        }
        return list;
    }

    public static byte[] setNotificationData(Notification notification, boolean isExists, boolean isOpen, int sequence) {
        byte[] packet;
        if (notification.type == BluetoothKey.AUTOMATION) {
            packet = new byte[26];
            packet[0] = 0x31;
            packet[1] = 22;
            packet[2] = formatBitForAdvance(isExists, isOpen, notification.id);
            packet[3] = (byte) notification.id;
            packet[4] = (byte) (notification.start / 60);
            packet[5] = (byte) (notification.start % 60);
            packet[6] = (byte) (notification.end / 60);
            packet[7] = (byte) (notification.end % 60);
            packet[8] = notification.model;
            int value = notification.cycleOn * 60;
            packet[9] = (byte) (value >> 24);
            packet[10] = (byte) (value >> 16);
            packet[11] = (byte) (value >> 8);
            packet[12] = (byte) value;
            value = notification.cycleOff * 60;
            packet[13] = (byte) (value >> 24);
            packet[14] = (byte) (value >> 16);
            packet[15] = (byte) (value >> 8);
            packet[16] = (byte) value;
            packet[17] = notification.tmpHum;
            packet[18] = notification.hTmpF;
            packet[19] = notification.hTmpC;
            packet[20] = notification.lTmpF;
            packet[21] = notification.lTmpC;
            packet[22] = notification.hHum;
            packet[23] = notification.lHum;
            packet[24] = (byte)0xff;
            packet[25] = notification.port;
        } else {
            packet = new byte[15];
            if (notification.type == BluetoothKey.ALARMS) {
                packet[0] = 0x32;
            } else {
                packet[0] = 0x33;
            }
            packet[1] = 11;
            packet[2] = formatBitForAdvance(isExists, isOpen, notification.id);
            packet[3] = notification.model;
            packet[4] = notification.tmpHum;
            packet[5] = notification.hTmpF;
            packet[6] = notification.hTmpC;
            packet[7] = notification.lTmpF;
            packet[8] = notification.lTmpC;
            packet[9] = notification.hHum;
            packet[10] = notification.lHum;
            packet[11] = 0;
            packet[12] = 0;
            packet[13] = (byte)0xff;
            packet[14] = notification.port;
        }
        return addHead(packet, BluetoothKey.COMMAND_SET, sequence);
    }

    /**
     * 格式化设置是否存在，开启状态等为Advance需要发送到byte
     *
     * @param isExists 是否存在
     * @param isOpen   是否打开
     * @param seridNo
     * @return
     */
    public static byte formatBitForAdvance(boolean isExists, boolean isOpen, int seridNo) {
        byte b = 0;
        b = (byte) ((b >> 7 | (isExists ? 1 : 0)) << 7);
        b = (byte) ((b >> 6 | (isOpen ? 1 : 0)) << 6);
        return (byte) (b | seridNo);
    }

    public static byte[] getSettingData(byte port,int sequence) {
        return addHead(new byte[]{
                BluetoothKey.DEVICE_TMP_UNIT,
                BluetoothKey.DEVICE_BACKLIGHT_BRINGHTNESS_GEAR,
                BluetoothKey.DEVICE_TEMP_HUM_TRANSITION,
                BluetoothKey.DEVICE_TEMP_HUM_CALIBRATION,
                (byte)0xFF, port
        }, BluetoothKey.COMMAND_GET, sequence);
    }

    public static byte[] setSettingData(byte port, DeviceSetting setting, int sequence) {
        byte[] data = new byte[18];
        //温度单位
        data[0] = 0x20;
        data[1] = 0x1;
        data[2] = (byte) (setting.isDegree ? 0x1 : 0x0);
        //背光亮度
        data[3] = 0x21;
        data[4] = 0x1;
        data[5] = setting.brightness;
        //transition 温度和湿度
        data[6] = 0x22;
        data[7] = 0x3;
        data[8] = 0x0;
        data[9] = 0x0;
        data[10] = setting.transitionHumidity;
        if (setting.isDegree) {
            data[9] = setting.transitionTemperature;
        } else {
            data[8] = setting.transitionTemperature;
        }
        //
        data[11] = 0x24;
        data[12] = 0x3;
        data[13] = 0x0;
        data[14] = 0x0;
        data[15] = setting.calibrationHumidity;
        if (setting.isDegree) {
            data[14] = setting.calibrationTemperature;
        } else {
            data[13] = setting.calibrationTemperature;
        }
        data[16] = (byte)0xFF;
        data[17] = port;
        return addHead(data, BluetoothKey.COMMAND_SET, sequence);
    }

    public static History parseHistory(byte[] packet, int num, long now, byte type) {
        History history = new History();
        history.time = now;
        history.tmp = ByteUtils.getShort(packet,num);
        if (type == 0x02) {
            history.hum = ((char) (((packet[num + 2] & 0xff) << 8) | (packet[num + 3] & 0xff)));
        } else if (type == 0x03){
            history.off=packet[num+2]==0;
            List<Boolean> offList =  new ArrayList<>();
            for (int i = 0; i < packet[num+3]; i++) {
                offList.add(packet[num+4+i]==0);
            }
            history.portStateList = offList;
        } else {
            history.hum = ((char) (((packet[num + 2] & 0xff) << 8) | (packet[num + 3] & 0xff)));
            history.off=packet[num+4]==0;
            List<Boolean> offList =  new ArrayList<>();
            for (int i = 0; i < packet[num+5]; i++) {
                offList.add(packet[num+6+i]==0);
            }
            history.portStateList = offList;
        }
        return history;
    }

    protected static byte[] check(byte[] packet) {
        byte[] data = new byte[packet.length - 10 - 2];
        System.arraycopy(packet, 10, data, 0, data.length);
        return data;
    }

    public static boolean checkResult(byte[] packet) {
        byte[] data = new byte[packet.length - 10 - 4];
        System.arraycopy(packet, 10, data, 0, data.length);
        for (int i = 1; i < data.length; i += 2) {
            if (data[i] == 1) {
                return false;
            }
        }
        return true;
    }

}
